package service

import (
	"GopherFocus/app/model"
	"GopherFocus/app/system/index/internal/define"
	"context"
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/net/ghttp"
	"github.com/gogf/gf/text/gstr"
	"github.com/gogf/gf/util/gconv"
	"github.com/gogf/gf/util/gmode"
	"path"
)

// 试图管理服务
var View = viewService{}

type viewService struct{}

const (
	Status302 = 302
	Status401 = 401
	Status403 = 403
	Status404 = 404
	Status500 = 500
)

var statusToMsg = map[int]string{
	Status302: "页面跳转中",
	Status401: "无访问权限",
	Status403: "无访问权限",
	Status404: "资源不存在",
	Status500: "请求执行错误",
}

// 渲染默认模板页面
func (s *viewService) Render(r *ghttp.Request, data ...model.View) {
	// 默认渲染index/index.html
	s.RenderTpl(r, g.Cfg().GetString("viewer.indexLayout"), data...)
}

// 渲染指定模板页面
func (s *viewService) RenderTpl(r *ghttp.Request, tpl string, data ...model.View) {
	var (
		viewObj  = model.View{}
		viewData = make(g.Map)
	)

	if len(data) > 0 {
		viewObj = data[0]
	}

	// 设置默认的title，keywords，description
	if viewObj.Title == "" {
		viewObj.Title = g.Cfg().GetString("setting.title")
	} else {
		viewObj.Title = viewObj.Title + `-` + g.Cfg().GetString(`settings.title`)
	}
	if viewObj.Keywords == "" {
		viewObj.Keywords = g.Cfg().GetString("setting.keywords")
	}
	if viewObj.Description == "" {
		viewObj.Description = g.Cfg().GetString("settings.description")
	}

	// 去掉空数据
	// model.View类型结构体转换为Map，如果结构体字段为空，则转换后map的值为对应类型的零值
	// gconv.Map返回map[string]interface{}类型， 与g.Map类型一致
	// WriteTpl和WriteTplContent传入模板变量时只接收map[string]interface{}类型
	viewData = gconv.Map(viewObj)
	for k, v := range viewData {
		if g.IsEmpty(v) {
			delete(viewData, k)
		}
	}

	// 内置对象
	viewData["BuildIn"] = &viewBuildIn{httpRequest: r}
	// 内容模板
	if viewData["MainTpl"] == nil {
		viewData["MainTpl"] = s.getDefaultMainTpl(r)
	}

	// 提示信息
	if notice, _ := Session.GetNotice(r.Context()); notice != nil {
		_ = Session.RemoveNotice(r.Context())
		viewData["Notice"] = notice
	}

	// ParseTplContent会根据data的值将模板语法解析成对应的数据, 返回一个字符串和一个error类型
	// WriteTplContent, 会根据data的值将模板语法解析成对应的数据然后写入到响应体中, 返回error类型
	_ = r.Response.WriteTpl(tpl, viewData)
	// {{dump .}} 会将所有模板变量格式化成JSON样式
	if gmode.IsDevelop() {
		_ = r.Response.WriteTplContent(`{{dump .}}`, viewData)
	}
	// 退出当前业务函数执行
	r.Exit()
}

// 获取面包屑导航, 每一个model.ViewBreadCrumb包含Name和Url两个字段
func (s *viewService) GetBreadCrumb(ctx context.Context, input *define.ViewGetBreadCrumbInput) []model.ViewBreadCrumb {
	// 首页固定名称和url
	breadCrumb := []model.ViewBreadCrumb{
		{Name: "首页", Url: "/"},
	}
	var uriPrefix string
	// 根据ContentType获取主题名称和url
	if input.ContentType != "" {
		uriPrefix = "/" + input.ContentType
		// 根据uri获取对应的顶部菜单(每个菜单元素包含Name，Url，Target等)
		topMenuItem, _ := Menu.GetTopMenuByUrl(ctx, uriPrefix)
		if topMenuItem != nil {
			breadCrumb = append(breadCrumb, model.ViewBreadCrumb{
				Name: topMenuItem.Name,
				Url:  topMenuItem.Url,
			})
		}
	}
	// 根据CategoryId获取栏目名称和url
	if uriPrefix != "" && input.CategoryId > 0 {
		// 获取单个category
		category, _ := Category.GetItem(ctx, input.CategoryId)
		if category != nil {
			breadCrumb = append(breadCrumb, model.ViewBreadCrumb{
				Name: category.Name,
				Url:  uriPrefix,
			})
		}
	}
	// 如果contentId大于0，则固定面包屑的Name为内容详情
	if input.ContentId > 0 {
		breadCrumb = append(breadCrumb, model.ViewBreadCrumb{
			Name: "内容详情",
		})
	}
	return breadCrumb
}

// 前台系统-获取组合标题，例如点击"主题"下的"分享"，则在浏览页标签上会显示 "分享-主题-focus聚焦社区"
func (s *viewService) GetTitle(ctx context.Context, input *define.ViewGetTitleInput) string {
	var (
		// 用于存储子栏目名，栏目名
		titleArray []string
		uriPrefix  string
	)
	// 当前子栏目名
	if input.CurrentName != "" {
		titleArray = append(titleArray, input.CurrentName)
	}
	// 获取父栏目名
	if input.CategoryId > 0 {
		category, _ := Category.GetItem(ctx, input.CategoryId)
		if category != nil {
			titleArray = append(titleArray, category.Name)
		}
	}
	// focus聚焦社区???
	// config.toml配置中title默认为"focus聚焦社区"
	if input.ContentType != "" {
		uriPrefix = "/" + input.ContentType
		topMenuItem, _ := Menu.GetTopMenuByUrl(ctx, uriPrefix)
		if topMenuItem != nil {
			titleArray = append(titleArray, topMenuItem.Name)
		}
	}
	return gstr.Join(titleArray, " - ")
}

// 获取自动设置的MainTpl
// TODO: 获取默认模板的逻辑待确认
func (s *viewService) getDefaultMainTpl(r *ghttp.Request) string {
	var (
		viewFolderPrefix = s.getViewFolderName()
		urlPathArray     = gstr.SplitAndTrim(r.URL.Path, "/")
		mainTpl          string
	)
	if len(urlPathArray) > 0 && urlPathArray[0] == viewFolderPrefix {
		urlPathArray = urlPathArray[1:]
	}
	switch {
	case len(urlPathArray) == 2:
		if gstr.IsNumeric(urlPathArray[1]) {
			urlPathArray[1] = "detail"
		}
		mainTpl = viewFolderPrefix + "/" + path.Join(urlPathArray[0], urlPathArray[1]) + ".html"
	case len(urlPathArray) == 1:
		mainTpl = viewFolderPrefix + "/" + urlPathArray[0] + "/index.html"
	default:
		mainTpl = viewFolderPrefix + "/index/index.html"
	}
	return gstr.TrimLeft(mainTpl, "/")
}

// 跳转中间页
func (s *viewService) Render302(r *ghttp.Request, data ...model.View) {
	s.RenderStatus(r, Status302, data...)
}

// 401页面
func (s *viewService) Render401(r *ghttp.Request, data ...model.View) {
	s.RenderStatus(r, Status401, data...)
}

// 403页面
func (s *viewService) Render403(r *ghttp.Request, data ...model.View) {
	s.RenderStatus(r, Status403, data...)
}

// 404页面
func (s *viewService) Render404(r *ghttp.Request, data ...model.View) {
	s.RenderStatus(r, Status404, data...)
}

// 500页面
func (s *viewService) Render500(r *ghttp.Request, data ...model.View) {
	s.RenderStatus(r, Status500, data...)
}

// 根据状态码渲染不同的页面
func (s *viewService) RenderStatus(r *ghttp.Request, code int, data ...model.View) {
	view := model.View{}
	if len(data) > 0 {
		view = data[0]
	}
	if view.Title == "" {
		view.Title = statusToMsg[code]
	}
	view.MainTpl = s.getViewFolderName() + "/pages/" + gconv.String(code) + ".html"
	s.Render(r, view)
}

//
func (s *viewService) getViewFolderName() string {
	// Split函数会根据分隔符分割字符串，SplitAndTrim分割后会将为空的元素剔除掉
	return gstr.Split(g.Cfg().GetString("viewer.indexLayout"), "/")[0]
}
